//
// Created on 2025/11/5.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#include "background_cache.h"
#include "background_cache_manager.h"
#include <filesystem>
#include <sstream>

static uint64_t ZERO = 0;

BackgroundCache::BackgroundCache(uint64_t frameCacheSize, uint32_t codecWidth, 
    uint32_t codecHeight, uint32_t codecFrameRate, OH_AVPixelFormat codecPixelFormat)
{
    PRE_EXPORT_LOGI("create %{public}p %{public}ld", this, frameCacheSize);
    cacheSize_.store(frameCacheSize);
    codecWidth_ = codecWidth;
    codecHeight_ = codecHeight;
    codecFrameRate_ = codecFrameRate;
    codecPixelFormat_ = codecPixelFormat;
    manager_ = BackgroundCacheManager::GetInstance();
    interrupted_.store(true);
    {
        std::lock_guard<std::mutex> lock(mutex_);
        cacheInfoList_ = new CacheInfo[GetSize()];
        // 初始每FrameRate一个I帧
        for(uint64_t i=0; i<GetSize(); i++) {
            if (i % GetFrameRate() == 0) {
                PRE_EXPORT_LOGI("init %{public}d as iframe", i);
                cacheInfoList_[i].isIFrame = true;
            }
        }
    }
}
    
BackgroundCache::~BackgroundCache()
{
    PRE_EXPORT_LOGI("delete %{public}p %{public}s", this, hashString_.c_str());
    {
        std::lock_guard<std::mutex> lock(mutex_);
        if (cacheInfoList_ != nullptr) {
            delete[] cacheInfoList_;
            cacheInfoList_ = nullptr;
        }
    }
    {
        std::lock_guard<std::mutex> lock(exportMutex_);
        exportCacheInfoMap_.clear();
    }
}
    
ResultCode BackgroundCache::SetHash(const std::string &hash)
{
    if (hashString_ != "") {
        PRE_EXPORT_LOGE("hash ready set");
        return ResultCode::CONFIGURE_ERROR;
    }
    hashString_ = hash;
    PRE_EXPORT_LOGI("hash %{public}s", hashString_.c_str());
    if (manager_ == nullptr) {
        PRE_EXPORT_LOGE("manager nullptr");
        return ResultCode::ERROR;
    }
    manager_->RecodeBackgroundCache(hashString_, shared_from_this());
    return ResultCode::OK;
}
   
ResultCode BackgroundCache::SetCachePath(const std::string &cachePath)
{
    if (cachePath_ != "") {
        PRE_EXPORT_LOGE("cachePath_ ready set");
        return ResultCode::CONFIGURE_ERROR;
    }
    cachePath_ = cachePath;
    PRE_EXPORT_LOGI("cachePath %{public}s", cachePath_.c_str());
    return ResultCode::OK;    
}

bool BackgroundCache::TestFrameValid(uint64_t frameIndex, ExportType exportType)
{
    CacheInfo *cacheInfo = GetCacheInfoByIndex(frameIndex, exportType);
    if (cacheInfo == nullptr) {
        return false;
    }
    return cacheInfo->isCached();
}

bool BackgroundCache::CheckFrameRange(uint64_t frameIndex, uint64_t *beginIndex, uint64_t *endIndex,
                                      ExportType exportType)
{
    // 范围内是否需要喂帧
    interrupted_.store(false);
    if (manager_ == nullptr) {
        PRE_EXPORT_LOGE("manager nullptr");
        return false;
    }
    manager_->start();
    uint64_t left = frameIndex;
    uint64_t right = frameIndex;
    uint64_t size = cacheSize_.load();
    if (frameIndex > *endIndex || frameIndex < *beginIndex || *endIndex >= size) {
        PRE_EXPORT_LOGE("bad index or range");
        return false;
    }
    if (exportType == ExportType::PRE_EXPORT) {
        // 预导出范围的I帧的更新
        if (UpdateIFrameRange(*beginIndex, *endIndex) != ResultCode::OK) {
            return false;
        }
    } else {
        // 先检查导出范围的失效区域
        if (UpdateExportInvalidRange(*beginIndex, *endIndex, exportType) != ResultCode::OK) {
            return false;
        }
    }

    bool valid = false;
    if (TestFrameNeedInputFrame(frameIndex, exportType)) {
        for (uint64_t i = frameIndex + 1; i <= *endIndex; i++) {
            if (!TestFrameNeedInputFrame(i,exportType)) {
                break;
            }
            right = i;
        }
        for (int64_t i = frameIndex - 1; i >= static_cast<int64_t>(*beginIndex); i--) {
            if (!TestFrameNeedInputFrame(static_cast<uint64_t>(i), exportType)) {
                break;
            }
            left = i;
        }
        valid = true;
    } else {
        for (uint64_t i = frameIndex + 1; i <= *endIndex; i++) {
            if (TestFrameNeedInputFrame(i, exportType)) {
                break;
            }
            right = i;
        }
        for (int64_t i = frameIndex - 1; i >= static_cast<int64_t>(*beginIndex); i--) {
            if (TestFrameNeedInputFrame(static_cast<uint64_t>(i), exportType)) {
                break;
            }
            left = i;
        }
    }
    // 仅返回检查范围内
    *beginIndex = left;
    *endIndex = right;
    return valid;
}

ResultCode BackgroundCache::PutFrame(uint64_t frameIndex, void *inFrameData, uint32_t frameWidth,
                    uint32_t frameHeight, uint32_t widthPitch, ExportType exportType)
{
    PRE_EXPORT_LOGI("cache %{public}ld[%{public}d, %{public}d], pitch %{public}d, exportType %{public}d",
                    frameIndex, frameWidth, frameHeight, widthPitch, static_cast<int>(exportType));
    interrupted_.store(false);
    if (inFrameData == nullptr || frameWidth == 0 || frameHeight == 0 || widthPitch == 0) {
        PRE_EXPORT_LOGE("inFrameData nullptr"); 
        return ResultCode::PARAM_ERROR;
    }
    if (manager_ == nullptr) {
        PRE_EXPORT_LOGE("manager nullptr");
        return ResultCode::ERROR;
    }
    manager_->start();
    uint32_t dataSize = widthPitch * frameHeight;
    uint8_t* newBuff = new uint8_t[dataSize];
    uint8_t* dst = newBuff;
    uint8_t* src = reinterpret_cast<uint8_t *>(inFrameData);
    for (uint32_t row = 0; row < frameHeight; row++) {
        for (uint32_t col = 0; col < widthPitch; col++) {
            *dst = *src;
            dst += 1;
            src += 1;
        }
        dst += frameWidth - widthPitch;
    }
    CacheInfo *cacheInfo = GetCacheInfoByIndex(frameIndex, exportType);
    cacheInfo->frameInput = true;
    cacheInfo->inBuff = newBuff;
    cacheInfo->width = widthPitch;
    cacheInfo->height = frameHeight;
    cacheInfo->isValid = true;
    return SetCacheInfoByIndex(frameIndex, cacheInfo, exportType);
}

ResultCode BackgroundCache::GetFrame(uint64_t frameIndex, 
    void **outFrameData, uint32_t *outFrameSize, ExportType exportType)
{
    // 检查帧缓存有效性及返回缓存数据
    PRE_EXPORT_LOGI("get frame %{public}ld", frameIndex);
    if (TestFrameValid(frameIndex, exportType)) {
        CacheInfo *cacheInfo = GetCacheInfoByIndex(frameIndex, exportType);
        if (cacheInfo == nullptr) {
            PRE_EXPORT_LOGE("get frame %{public}ld failed", frameIndex);
            return ResultCode::ERROR;
        }
        PRE_EXPORT_LOGI("get frame %{public}ld, path %{public}s", frameIndex, cacheInfo->cachePath.c_str());
        return CodecCommon::ReadFile(cacheInfo->cachePath, outFrameData, outFrameSize);
    } else {
        PRE_EXPORT_LOGW("get frame %{public}ld invalid", frameIndex);
        return ResultCode::ERROR;
    }
}

ResultCode BackgroundCache::NotifyInterrupt()
{
    PRE_EXPORT_LOGI("interrupted");
    interrupted_.store(true);
    return ResultCode::OK;
}

bool BackgroundCache::IsInterrupt()
{
    return interrupted_.load();
}

std::string BackgroundCache::GetCachePath(ExportType exportType)
{
    if (cachePath_ == "") {
        return cachePath_;
    }
    std::filesystem::path p = std::filesystem::u8path(
        cachePath_) / hashString_ / std::to_string(static_cast<int>(exportType));
    if (!std::filesystem::exists(p)) {
        std::filesystem::create_directories(p);
    }
    return std::filesystem::canonical(p);
}

uint64_t BackgroundCache::GetSize()
{
    return cacheSize_.load();
}

ResultCode BackgroundCache::Resize(uint64_t size)
{
    if (size == 0 || size > cacheSize_.load()) {
        return ResultCode::PARAM_ERROR;
    }
    cacheSize_.store(size);
    return ResultCode::OK;
}

CacheInfo* BackgroundCache::GetCacheInfoByIndex(uint64_t frameIndex, ExportType exportType)
{
    if (frameIndex > cacheSize_.load() || cacheInfoList_ == nullptr) {
        PRE_EXPORT_LOGE("param invalid or cacheInfoList_ nullptr: index %{public}ld size %{public}ld",
                        frameIndex, cacheSize_.load());
        return nullptr;
    }
    if (exportType == ExportType::PRE_EXPORT) {
        std::lock_guard<std::mutex> lock(mutex_);
        return &cacheInfoList_[frameIndex];
    } else {
        auto it = exportCacheInfoMap_.find(frameIndex);
        if (it != exportCacheInfoMap_.end()) {
            return &it->second;
        } else {
            return GetCacheInfoByIndex(frameIndex, ExportType::PRE_EXPORT);
        }
    }
}

ResultCode BackgroundCache::SetCacheInfoByIndex(uint64_t frameIndex, CacheInfo* cacheInfo, ExportType exportType)
{
    // set force
    auto cacheSize = cacheSize_.load();
    if (frameIndex >= cacheSize) {
        PRE_EXPORT_LOGE("param invalid");
        return ResultCode::PARAM_ERROR;
    }
    if (exportType == ExportType::PRE_EXPORT) {
        std::lock_guard<std::mutex> lock(mutex_);
        if (cacheInfoList_ == nullptr) {
            PRE_EXPORT_LOGE("cacheInfoList_ nullptr");
            return ResultCode::ERROR; 
        }
        cacheInfoList_[frameIndex] = *cacheInfo;
    } else {
        std::lock_guard<std::mutex> lock(exportMutex_);
        if (exportCacheInfoMap_.find(frameIndex) != exportCacheInfoMap_.end()) {
            exportCacheInfoMap_[frameIndex] = *cacheInfo;
            return ResultCode::OK;
        }
        return SetCacheInfoByIndex(frameIndex, cacheInfo, ExportType::PRE_EXPORT);
    }

    return ResultCode::OK;
}

bool BackgroundCache::CheckEnCodeRange(uint64_t *frameStart, uint64_t *frameEnd, ExportType exportType)
{
    if (exportType == ExportType::PRE_EXPORT) {
        return CheckPreExportEnCodeRange(frameStart, frameEnd);
    } else {
        return CheckExportEnCodeRange(frameStart, frameEnd);
    }
}

bool BackgroundCache::CheckPreExportEnCodeRange(uint64_t *frameStart, uint64_t *frameEnd)
{
    // 预导出的I帧间隔一编码
    ExportType exportType = ExportType::PRE_EXPORT;
    {
        std::lock_guard<std::mutex> lock(mutex_);
        if (cacheInfoList_ == nullptr) {
            PRE_EXPORT_LOGE("cacheInfoList_ nullptr");
            return false;
        }
    }
    uint64_t pos = 0;
    uint64_t size = cacheSize_.load();
    int64_t lastIFrame = -1;
    bool lastIFrameReadyToCache = false;
    while (pos < size) {
        CacheInfo *cacheInfo = GetCacheInfoByIndex(pos, exportType);
        if (cacheInfo == nullptr) {
            PRE_EXPORT_LOGE("get frame %{public}ld failed", pos);
            return false;
        }
        if (cacheInfo->isIFrame) {
            if (lastIFrame >= 0 && lastIFrameReadyToCache) {
                *frameStart = lastIFrame;
                *frameEnd = pos - 1;
                return true;
            }
            lastIFrame = pos;
            lastIFrameReadyToCache = cacheInfo->isReadyToCache();
            pos += 1;
            continue;
        } else {
            if (!lastIFrameReadyToCache || !cacheInfo->isReadyToCache()) {
                lastIFrameReadyToCache = false;
            }
            pos += 1;
        }
    }
    if (lastIFrame >= 0 && lastIFrameReadyToCache) {
        *frameStart = lastIFrame;
        *frameEnd = size - 1;
        return true;
    }
    return false;
}

bool BackgroundCache::CheckExportEnCodeRange(uint64_t *frameStart, uint64_t *frameEnd)
{
    // 导出的每个连片段一次编码
    ExportType exportType = ExportType::EXPORT;
    uint64_t pos = 0;
    uint64_t size = cacheSize_.load();
    int64_t encodeStart = -1;
    bool lastReadyToCached = false;
    while (pos < size) {
        CacheInfo* cacheInfo = nullptr;
        auto it = exportCacheInfoMap_.find(pos);
        if (it != exportCacheInfoMap_.end()) {
            cacheInfo = &it->second;
        }
        if (cacheInfo == nullptr) {
            if (encodeStart == -1) {
                pos += 1;
                continue;
            } else {
                if (!lastReadyToCached) {
                    pos += 1;
                    encodeStart = -1;
                    continue;
                }
                *frameStart = encodeStart;
                *frameEnd = pos - 1;
                return true;
            }
        }
        if (encodeStart == -1) {
            encodeStart = pos;
            lastReadyToCached = cacheInfo->isReadyToCache();
        }
        if (!lastReadyToCached || !cacheInfo->isReadyToCache()) {
            lastReadyToCached = false;
        }
        pos += 1;
        continue;
    }
    return false;
}

ResultCode BackgroundCache::RunUntilEncodeFinished(uint64_t timeoutInSecond)
{
    // 已喂帧的编码完成
    auto startTime = std::chrono::duration_cast<std::chrono::seconds>(
        std::chrono::system_clock::now().time_since_epoch()).count();
    PRE_EXPORT_LOGI("start at %{public}lld", startTime);
    if (manager_ == nullptr) {
        PRE_EXPORT_LOGE("manager nullptr");
        return ResultCode::ERROR;
    }
    interrupted_.store(false);
    manager_->start();
    auto now = startTime;
    auto timeout = startTime + timeoutInSecond;
    bool isTimeout = true;
    while (now < timeout) {
        now = std::chrono::duration_cast<std::chrono::seconds>(
            std::chrono::system_clock::now().time_since_epoch()).count();
        uint64_t start = 0;
        uint64_t end = GetSize() - 1;
        uint64_t pos = 0;
        uint64_t finished = true;
        while (pos <= end) {
            finished = false;
            auto cache = GetCacheInfoByIndex(pos, ExportType::EXPORT);
            if (cache == nullptr || (!cache->frameInput && !cache->isCached())) {
                pos += 1;
                continue;
            }
            uint64_t cachedStart = pos;
            uint64_t cachedEnd = GetSize() - 1;
            if (CheckCachedFrameRange(cachedStart,&cachedStart, &cachedEnd, ExportType::EXPORT)) {
                if (cachedStart == pos && cachedEnd == end) {
                    finished = true;
                    break;
                }
                pos = cachedEnd + 1;
            } else {
                finished = false;
                break;
            }     
        }
        if (finished) {
            PRE_EXPORT_LOGI("finish at %{public}lld", now);
            isTimeout = false;
            break;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    if (isTimeout) {
        PRE_EXPORT_LOGI("timeout at %{public}lld", startTime);
        return ResultCode::TIMEOUT_ERROR;
    }
    return ResultCode::OK;
}

ResultCode BackgroundCache::CodecResultHandle(uint64_t start, uint64_t end, 
    std::unordered_map<uint64_t, std::string>& result, ExportType exportType)
{
    PRE_EXPORT_LOGI("codec encode finished with [%{public}ld, %{public}ld], size: %{public}ld", 
                    start, end, result.size());
    for (uint64_t i=start; i<=end; i++) {
        auto iter = result.find(i + 1);
        if (iter != result.end()) {
            CacheInfo *cacheInfo2 = GetCacheInfoByIndex(i, exportType);
            if (cacheInfo2 == nullptr) {
                PRE_EXPORT_LOGE("get frame %{public}ld failed", i);
                break;
            }
            cacheInfo2->cached = true;
            cacheInfo2->cachePath = iter->second;
            auto inBuff = cacheInfo2->inBuff;
            if (inBuff != nullptr) {
                free(inBuff);
                cacheInfo2->inBuff = nullptr;
            }
        }
    }
    return ResultCode::OK;
}

ResultCode BackgroundCache::UpdateExportInvalidRange(
     uint64_t beginIndex, uint64_t endIndex, ExportType exportType)
{
    auto size = GetSize();
    if (size == 0) {
        return ResultCode::CACHE_INVALID;
    }
    // 区域起始结束不在I帧的
    if (exportType != ExportType::EXPORT) {
        return ResultCode::OK;
    }
    if (endIndex >= size || beginIndex > endIndex ) {
        PRE_EXPORT_LOGE("bad param: %{public}ld, %{public}ld", beginIndex, endIndex);
        return ResultCode::PARAM_ERROR;
    }
    // 起始非I帧到下一I帧都需重新编码
    for(uint64_t i=beginIndex; i<=endIndex; i++) {
        CacheInfo *cacheInfo = GetCacheInfoByIndex(i, ExportType::PRE_EXPORT);
        if (cacheInfo->isIFrame) {
            break;
        }
        CacheInfo *cacheInfo2 = nullptr;
        auto it=exportCacheInfoMap_.find(i);
        if (it != exportCacheInfoMap_.end()) {
            cacheInfo2 = &it->second;
        }
        if (cacheInfo2 == nullptr) {
            cacheInfo2 = new CacheInfo;
            if (cacheInfo2 == nullptr) {
                PRE_EXPORT_LOGE("new CacheInfo error");
                return ResultCode::SYSTEM_ERROR;
            }
            PRE_EXPORT_LOGI("add to exportCacheInfoMap_: %{public}ld", i);
            exportCacheInfoMap_[i] = *cacheInfo2;
        }
    }
    
    // 尾帧视为下一帧为I帧
    bool endIndexNextFrameIsIFrame = true;
    if (endIndex < size - 1) {
        CacheInfo *cacheInfo = GetCacheInfoByIndex(endIndex + 1, ExportType::PRE_EXPORT);
        endIndexNextFrameIsIFrame = cacheInfo->isIFrame;
    }
    if (!endIndexNextFrameIsIFrame) {
        // 结尾非I帧，如果时 到下一I帧都需重新编码
        bool isLastIFrame = false;
        for(int64_t i=endIndex; i>=beginIndex; i--) {
            if (isLastIFrame) {
                break;
            }
            CacheInfo *cacheInfo = GetCacheInfoByIndex(i, ExportType::PRE_EXPORT);
            if (cacheInfo->isIFrame) {
                isLastIFrame = true;
            }
            CacheInfo *cacheInfo2 = nullptr;
            auto it=exportCacheInfoMap_.find(i);
            if (it != exportCacheInfoMap_.end()) {
                cacheInfo2 = &it->second;
            }
            if (cacheInfo2 == nullptr) {
                cacheInfo2 = new CacheInfo;
                if (cacheInfo2 == nullptr) {
                    PRE_EXPORT_LOGE("new CacheInfo error");
                    return ResultCode::SYSTEM_ERROR;
                }
                PRE_EXPORT_LOGI("add to exportCacheInfoMap_: %{public}ld", i);
                exportCacheInfoMap_[i] = *cacheInfo2;
            }
        }
    }
    return ResultCode::OK;
}

ResultCode BackgroundCache::ExportFinished()
{
    PRE_EXPORT_LOGI("clear export cache map");
    std::lock_guard<std::mutex> lock(exportMutex_);
    exportCacheInfoMap_.clear();
    return ResultCode::OK;
}

ResultCode BackgroundCache::UpdateIFrameRange(uint64_t start, uint64_t end)
{
    if (cacheInfoList_ == nullptr || start >= end || end > GetSize()) {
        PRE_EXPORT_LOGE("bad param or cacheInfoList_ nullptr");
        return ResultCode::PARAM_ERROR;
    }
    auto frameRate = GetFrameRate();
    if (start % frameRate != 0) {
        if (!cacheInfoList_[start].isIFrame) {
            PRE_EXPORT_LOGI("update %{public}d as iframe", start);
            cacheInfoList_[start].isIFrame = true;
            cacheInfoList_[start].toReCache();
        }
    }
    if (end % frameRate != frameRate - 1 && end < GetSize() - 1) {
        if (!cacheInfoList_[end + 1].isIFrame) {
            PRE_EXPORT_LOGI("update %{public}d as iframe", end + 1);
            cacheInfoList_[end + 1].isIFrame = true;
            cacheInfoList_[end + 1].toReCache();
        }
    }
    return ResultCode::OK;
}

bool BackgroundCache::TestFrameNeedInputFrame(uint64_t frameIndex, ExportType exportType)
{
    CacheInfo *cacheInfo = GetCacheInfoByIndex(frameIndex, exportType);
    if (cacheInfo == nullptr) {
        return false;
    }
    return cacheInfo->isCached() || cacheInfo->isReadyToCache();
}

bool BackgroundCache::CheckCachedFrameRange(
    uint64_t frameIndex, uint64_t *beginIndex, uint64_t *endIndex, ExportType exportType) {
    if (beginIndex == nullptr || endIndex == nullptr) {
        PRE_EXPORT_LOGE("beginIndex or endIndex nullptr");
        return false;
    }
    uint64_t size = GetSize();
    if (size == 0) {
        return false;
    }
    *beginIndex = 0;
    *endIndex = size - 1;
    uint64_t left = frameIndex;
    uint64_t right = frameIndex;
    bool valid = false;
    if (TestFrameValid(frameIndex, exportType)) {
        for (uint64_t i = frameIndex + 1; i < size; i++) {
            if (!TestFrameValid(i,exportType)) {
                break;
            }
            right = i;
        }
        for (int64_t i = frameIndex - 1; i >= 0; i--) {
            if (!TestFrameValid(i, exportType)) {
                break;
            }
            left = i;
        }
        valid = true;
    } else {
        for (uint64_t i = frameIndex + 1; i < size; i++) {
            if (TestFrameValid(i, exportType)) {
                break;
            }
            right = i;
        }
        for (int64_t i = frameIndex - 1; i >= 0; i--) {
            if (TestFrameValid(i, exportType)) {
                break;
            }
            left = i;
        }
    }
    // 仅返回检查范围内
    *beginIndex = std::max(*beginIndex, left);
    *endIndex = std::min(*endIndex, right);
    return valid;
}